/*

    random.cpp - implementation of the random class
    Author Julio Agustin Barrera Mendoza
    Copyright (C) 2009 CINVESTAV-IPN.

    This program is free software; you can redistribute it and/or modify it 
    under the terms of the GNU General Public License as published by 
    the Free Software Foundation; either version 2 of the License, 
    or (at your option) any later version.

    This program is distributed in the hope that it will be useful, 
    but WITHOUT ANY WARRANTY; without even the implied warranty of 
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 
    General Public License for more details.

    You should have received a copy of the GNU General Public License along
    with this program; if not, write to 
    the Free Software Foundation, Inc., 59 Temple Place, 
    Suite 330, Boston, MA 02111-1307 USA

*/


#include "random.h"

// ------------------------------------------------------------------
// FUNCTION Random::Random
// ------------------------------------------------------------------

Random::Random()
{
  driver = "mt";
  seedSource = "default";
  dsfmt_init_gen_rand(&dsfmt, 1234567890);
  state = 1234567890;
}

// ------------------------------------------------------------------
// FUNCTION Random::~Random
// ------------------------------------------------------------------

Random::~Random()
{
  if (seedSource == "seedsfile")
    dumpSeeds(seedFilename);
}

// ------------------------------------------------------------------
// FUNCTION Random::useRand
// ------------------------------------------------------------------

void Random::useRand(void)
{
  driver = "rand";
  srand(dsfmt_genrand_uint32(&dsfmt));
}

// ------------------------------------------------------------------
// FUNCTION Random::useRandom
// ------------------------------------------------------------------

void Random::useRandom(void)
{
  driver = "random";
  srandom(dsfmt_genrand_uint32(&dsfmt));
}

// ------------------------------------------------------------------
// FUNCTION Random::useDrand48
// ------------------------------------------------------------------

void Random::useDrand48(void)
{
  driver = "drand48";
}

// ------------------------------------------------------------------
// FUNCTION Random::useMt
// ------------------------------------------------------------------

void Random::useMt(void)
{
  driver = "mt";
}

// ------------------------------------------------------------------
// FUNCTION Random::setSeedSource
// ------------------------------------------------------------------

void Random::setSeedSource(const string &source, const string &filename)
{
  if (source == "seedsfile")
    {    
      seedFilename = filename;
      seedSource = "seedsfile";
      loadSeeds(seedFilename);
    }
}

// ------------------------------------------------------------------
// FUNCTION Random::loadSeeds
// ------------------------------------------------------------------

void Random::loadSeeds(const string &filename)
{
  ifstream input;
  string line;

  input.open(filename.c_str());

  while (getline(input, line))
    {
      istringstream inputString(line);
      uint32_t value;
    
      inputString >> value;

      if (!inputString.fail())
        seeds.push_back(value);
    }

  input.close();  
}

// ------------------------------------------------------------------
// FUNCTION Random::dumpSeeds
// ------------------------------------------------------------------

void Random::dumpSeeds(const string &filename)
{
  unsigned int i;
  ofstream output;

  output.open(filename.c_str());

  for (i = 0; i < seeds.size(); i++)
    output << seeds[i] << endl;
    
  output.close();
}

// ------------------------------------------------------------------
// FUNCTION Random::seed
// ------------------------------------------------------------------

void Random::seed(void)
{
  if (seedSource == "default")
    {
      uint32_t randomSeed = time(NULL);
      
      dsfmt_init_gen_rand(&dsfmt, randomSeed);
      srand48(randomSeed);
      state = randomSeed;
    }
  else if (seedSource == "seedsfile" && seeds.size() > 1)
    {
      dsfmt_init_gen_rand(&dsfmt, seeds.back());
      state = seeds.back();
      srand48(seeds.back());
      seeds.pop_back();
    }
}

// ------------------------------------------------------------------
// FUNCTION Random::Uniform
// ------------------------------------------------------------------

long double Random::Uniform(void)
{
  if (driver == "mt")
    {
      return dsfmt_genrand_close_open(&dsfmt);
    }
  else if (driver == "rand")
    {
      return static_cast<double>(rand()) / static_cast<double>(RAND_MAX);
    }
  else if (driver == "random")
    {
      return static_cast<double>(random()) / 2147483647.0;
    }
  else if (driver == "drand48")
    {
      return drand48();
    }

  return dsfmt_genrand_close_open(&dsfmt);
}

// ------------------------------------------------------------------
// FUNCTION Random::Normal
// ------------------------------------------------------------------

long double Random::Normal(long double mean, long double standardDeviation)
{
  long double x, y, w, result;

  do
    {
      x = 2.0 * Uniform() - 1.0;
      y = 2.0 * Uniform() - 1.0;
      
      w = x * x + y * y;
    }
  while (w >= 1.0);
  
  w = sqrtl(-2.0 * logl(w) / w);
  
  result = mean + x * w * standardDeviation;
  
  return result;
}

// ------------------------------------------------------------------
// FUNCTION Random::Integer
// ------------------------------------------------------------------

unsigned int Random::Integer(void)
{
  return dsfmt_genrand_uint32(&dsfmt);
}

// ------------------------------------------------------------------
// FUNCTION Random::seedFromFile
// ------------------------------------------------------------------

void Random::seedFromFile(const string &filename)
{
  unsigned int randomSeed;
  ifstream input;

  input.open(filename.c_str());
  input >> randomSeed;
  input.close();

  dsfmt_init_gen_rand(&dsfmt, randomSeed);

  state = randomSeed;
}

